﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using DotNetApp.Toolkit.Utilities;
using GalaSoft.MvvmLight.Threading;
using Microsoft.Phone.Scheduler;
using Microsoft.Phone.Shell;
using Newtonsoft.Json;
using WinSource.Client;
using WinSource.Model;
using WinSource.Resources;

namespace WinSource.Services
{
    public class DataService
    {
        #region Constants

        private const string ArticlesFilename = "Articles.txt";
        private const string SavedArticlesFilename = "SavedArticles.txt";
        private const string VideosFilename = "Videos.txt";
        private const string AgentName = "WinSourceAgent";

        #endregion

        #region Fields

        private readonly WinSourceClient _winSourceClient = new WinSourceClient();

        #endregion

        #region Constructor

        public DataService()
        {
            NetworkInformationUtility.GetNetworkTypeCompleted += GetNetworkTypeCompleted;

            InitializeWriters();
            InitializeLiveTile();
        }

        #endregion

        #region Properties

        public Settings Settings { get; private set; }
        public bool IsDataLoaded { get; private set; }
        public bool IsRefreshingArticles { get; private set; }
        public List<Article> Articles { get; private set; }
        public Article CurrentArticle { get; private set; }
        public bool CanMoveToPreviousArticle { get; private set; }
        public bool CanMoveToNextArticle { get; private set; }
        public bool CanSaveCurrentArticle { get; private set; }
        public List<Article> SavedArticles { get; private set; }
        public Article CurrentSavedArticle { get; private set; }
        public bool CanMoveToPreviousSavedArticle { get; private set; }
        public bool CanMoveToNextSavedArticle { get; private set; }
        public bool IsRefreshingVideos { get; private set; }
        public List<Video> Videos { get; private set; }
        public Video CurrentVideo { get; private set; }
        public Writer WriterJoe { get; private set; }
        public Writer WriterAshley { get; private set; }
        
        #endregion

        #region Methods

        public void Load()
        {
            LoadArticles();
            LoadSavedArticles();
            LoadVideos();
            LoadSettings();

            if (Settings.IsRefreshAutomatic)
            {
                RefreshData(true);
            }
        }

        public void Save()
        {
            SaveSettings();

            UpdateAgent();
        }

        public bool CanRefreshData()
        {
            return !IsRefreshingArticles && !IsDataLoaded;
        }

        public void RefreshData(bool isForce)
        {
            if (!IsRefreshingArticles && (!IsDataLoaded || isForce))
            {
                IsRefreshingArticles = true;

                NetworkInformationUtility.GetNetworkTypeAsync(3000);
            }
        }

        public void ReadArticle(Article article)
        {
            SetCurrentArticle(article);
        }

        public void ReadSavedArticle(Article article)
        {
            SetCurrentSavedArticle(article);
        }

        public void ViewVideo(Video video)
        {
            CurrentVideo = video;
        }

        #endregion

        #region Events

        public event EventHandler<DataRefreshedEventArgs> OnDataRefreshed;
        public event EventHandler<ArticleChangedEventArgs> OnArticleChanged;
        public event EventHandler OnSavedArticlesChanged;
        public event EventHandler<ArticleChangedEventArgs> OnSavedArticleChanged;

        #endregion

        #region Event Handlers

        private void GetNetworkTypeCompleted(object sender, NetworkTypeEventArgs networkTypeEventArgs)
        {
            DispatcherHelper.UIDispatcher.BeginInvoke(
                delegate
                    {
                        if (networkTypeEventArgs.HasInternet)
                        {
                            _winSourceClient.GetArticles(GetArticlesCallback);
                            _winSourceClient.GetVideos(GetVideosCallback);
                        }
                        else
                        {
                            IsRefreshingArticles = false;
                            IsRefreshingVideos = false;

                            NotifyDataRefreshed(DataType.Articles, false);
                        }
                    });
        }

        #endregion

        #region Callbacks

        private void GetArticlesCallback(GetArticlesRequestArgs getArticlesRequestArgs)
        {
            DispatcherHelper.UIDispatcher.BeginInvoke(
                delegate
                    {
                        if (getArticlesRequestArgs.IsSuccess)
                        {
                            Articles = getArticlesRequestArgs.Articles;

                            SaveArticles();

                            IsDataLoaded = true;
                        }

                        IsRefreshingArticles = false;

                        NotifyDataRefreshed(DataType.Articles, getArticlesRequestArgs.IsSuccess);
                    });
        }

        private void GetVideosCallback(GetVideosRequestArgs getVideosRequestArgs)
        {
            DispatcherHelper.UIDispatcher.BeginInvoke(
                delegate
                    {
                        if (getVideosRequestArgs.IsSuccess)
                        {
                            Videos = getVideosRequestArgs.Videos;

                            SaveVideos();
                        }

                        IsRefreshingVideos = false;

                        NotifyDataRefreshed(DataType.Videos, getVideosRequestArgs.IsSuccess);
                    });
        }

        #endregion

        #region Private Methods

        #region Articles

        public void LoadArticles()
        {
            try
            {
                using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (isolatedStorageFile.FileExists(ArticlesFilename))
                    {
                        using (IsolatedStorageFileStream stream = isolatedStorageFile.OpenFile(ArticlesFilename, FileMode.Open))
                        {
                            using (TextReader textReader = new StreamReader(stream))
                            {
                                string json = textReader.ReadToEnd();

                                Articles = JsonConvert.DeserializeObject<List<Article>>(json);
                            }
                        }
                    }
                }

                if (Articles == null || Articles.Count == 0)
                {
                    Articles = new List<Article>();
                }
                else
                {
                    IsDataLoaded = true;
                }
            }
            catch
            {
                Articles = new List<Article>();
            }
        }

        public void SaveArticles()
        {
            try
            {
                using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    using (IsolatedStorageFileStream stream = isolatedStorageFile.CreateFile(ArticlesFilename))
                    {
                        using (TextWriter textWriter = new StreamWriter(stream))
                        {
                            string jsonArticles = JsonConvert.SerializeObject(Articles);

                            textWriter.Write(jsonArticles);
                        }
                    }
                }
            }
            catch
            {
            }
        }

        public void SetCurrentArticle(Article article)
        {
            if (CurrentArticle != article)
            {
                CurrentArticle = article;

                UpdateArticleMoveStates();
                
                NotifyCurrentArticleChanged(CurrentArticle);
            }
        }

        public void MoveToPreviousArticle()
        {
            for (int articleIndex = 0; articleIndex < Articles.Count; articleIndex++)
            {
                if (Articles[articleIndex].Id == CurrentArticle.Id)
                {
                    if (articleIndex - 1 >= 0)
                    {
                        SetCurrentArticle(Articles[articleIndex - 1]);
                    }

                    break;
                }
            }
        }

        public void MoveToNextArticle()
        {
            for (int articleIndex = 0; articleIndex < Articles.Count; articleIndex++)
            {
                if (Articles[articleIndex].Id == CurrentArticle.Id)
                {
                    if (articleIndex + 1 < Articles.Count)
                    {
                        SetCurrentArticle(Articles[articleIndex + 1]);
                    }

                    break;
                }
            }
        }

        public void LoadSavedArticles()
        {
            try
            {
                using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (isolatedStorageFile.FileExists(SavedArticlesFilename))
                    {
                        using (IsolatedStorageFileStream stream = isolatedStorageFile.OpenFile(SavedArticlesFilename, FileMode.Open))
                        {
                            using (TextReader textReader = new StreamReader(stream))
                            {
                                string json = textReader.ReadToEnd();

                                SavedArticles = JsonConvert.DeserializeObject<List<Article>>(json);
                            }
                        }
                    }
                }

                if (SavedArticles == null || SavedArticles.Count == 0)
                {
                    SavedArticles = new List<Article>();
                }
            }
            catch
            {
                SavedArticles = new List<Article>();
            }
        }

        public void SaveSavedArticles()
        {
            try
            {
                using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    using (IsolatedStorageFileStream stream = isolatedStorageFile.CreateFile(SavedArticlesFilename))
                    {
                        using (TextWriter textWriter = new StreamWriter(stream))
                        {
                            string jsonArticles = JsonConvert.SerializeObject(SavedArticles);

                            textWriter.Write(jsonArticles);
                        }
                    }
                }
            }
            catch
            {
            }
        }

        public void SetCurrentSavedArticle(Article article)
        {
            if (CurrentSavedArticle != article)
            {
                CurrentSavedArticle = article;

                UpdateSavedArticleMoveStates();

                NotifyCurrentSavedArticleChanged(CurrentSavedArticle);
            }
        }

        public void MoveToPreviousSavedArticle()
        {
            for (int articleIndex = 0; articleIndex < SavedArticles.Count; articleIndex++)
            {
                if (SavedArticles[articleIndex].Id == CurrentSavedArticle.Id)
                {
                    if (articleIndex - 1 >= 0)
                    {
                        SetCurrentSavedArticle(SavedArticles[articleIndex - 1]);
                    }

                    break;
                }
            }
        }

        public void MoveToNextSavedArticle()
        {
            for (int articleIndex = 0; articleIndex < SavedArticles.Count; articleIndex++)
            {
                if (SavedArticles[articleIndex].Id == CurrentSavedArticle.Id)
                {
                    if (articleIndex + 1 < SavedArticles.Count)
                    {
                        SetCurrentSavedArticle(SavedArticles[articleIndex + 1]);
                    }

                    break;
                }
            }
        }

        public void SaveCurrentArticle(Article article)
        {
            if (!IsArticleAlreadySaved(article))
            {
                CanSaveCurrentArticle = false;

                SavedArticles.Add(article);

                SaveSavedArticles();

                NotifyArticlesChanged();
            }
        }

        public void DeleteAllSavedArticles()
        {
            SavedArticles.Clear();
            CurrentSavedArticle = null;

            using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (isolatedStorageFile.FileExists(SavedArticlesFilename))
                {
                    isolatedStorageFile.DeleteFile(SavedArticlesFilename);
                }
            }

            CanSaveCurrentArticle = true;

            NotifyArticlesChanged();
        }

        public void DeleteSavedArticle(Article article)
        {
            SavedArticles.Remove(article);
            CurrentSavedArticle = SavedArticles.Count > 0 ? SavedArticles[0] : null;

            SaveSavedArticles();

            UpdateSavedArticleMoveStates();

            NotifyCurrentSavedArticleChanged(CurrentSavedArticle);
            NotifyArticlesChanged();
        }

        public bool IsArticleAlreadySaved(Article article)
        {
            return SavedArticles.Any(savedArticle => savedArticle.Id == article.Id);
        }

        #endregion

        #region Videos

        private void LoadVideos()
        {
            try
            {
                using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (isolatedStorageFile.FileExists(VideosFilename))
                    {
                        using (IsolatedStorageFileStream stream = isolatedStorageFile.OpenFile(VideosFilename, FileMode.Open))
                        {
                            using (TextReader textReader = new StreamReader(stream))
                            {
                                string json = textReader.ReadToEnd();

                                Videos = JsonConvert.DeserializeObject<List<Video>>(json);
                            }
                        }
                    }
                }

                if (Videos == null || Videos.Count == 0)
                {
                    Videos = new List<Video>();
                }
            }
            catch
            {
                Videos = new List<Video>();
            }

            if (Videos.Count == 0)
            {
                IsDataLoaded = false;
            }
        }

        private void SaveVideos()
        {
            try
            {
                using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    using (IsolatedStorageFileStream stream = isolatedStorageFile.CreateFile(VideosFilename))
                    {
                        using (TextWriter textWriter = new StreamWriter(stream))
                        {
                            string json = JsonConvert.SerializeObject(Videos);

                            textWriter.Write(json);
                        }
                    }
                }
            }
            catch
            {
            }
        }

        #endregion

        private void InitializeWriters()
        {
            WriterJoe = new Writer("Joe Fedewa", Resource.DescriptionJoe, "/Assets/Views/Main/HubJoe.png", "joe@winsource.com");
            WriterAshley = new Writer("Ashley King", Resource.DescriptionAshley, "/Assets/Views/Main/HubAshley.png", "ashley@winsource.com");
        }

        private void InitializeLiveTile()
        {
            IsolatedStorageSettings.ApplicationSettings["LastNewArticlesCount"] = 0;

            ShellTile appTile = ShellTile.ActiveTiles.First();

            if (appTile != null)
            {
                appTile.Update(new StandardTileData {Count = 0});
            }
        }

        private void UpdateAgent()
        {
            if (Settings.IsDownloadingArticlesOffline)
            {
                StartAgent();
            }
            else
            {
                StopAgentIfStarted();
            }
        }

        private static void StartAgent()
        {
            StopAgentIfStarted();

            try
            {
                ScheduledActionService.Add(new PeriodicTask(AgentName) { Description = "Periodically download articles in the background if the Internet connection is Wi-Fi or Ethernet." });

#if DEBUG
                ScheduledActionService.LaunchForTest(AgentName, new TimeSpan(0, 0, 0, 3));
#endif
            }
            catch
            {
            }
        }

        private static void StopAgentIfStarted()
        {
            if (ScheduledActionService.Find(AgentName) != null)
            {
                ScheduledActionService.Remove(AgentName);
            }
        }

        private void LoadSettings()
        {
            if (IsolatedStorageSettings.ApplicationSettings.Contains("Settings"))
            {
                Settings = (Settings)IsolatedStorageSettings.ApplicationSettings["Settings"];
            }
            else
            {
                Settings = new Settings();
            }

            if (IsolatedStorageSettings.ApplicationSettings.Contains("CurrentArticle"))
            {
                CurrentArticle = (Article)IsolatedStorageSettings.ApplicationSettings["CurrentArticle"];

                UpdateArticleMoveStates();
            }

            if (IsolatedStorageSettings.ApplicationSettings.Contains("CurrentSavedArticle"))
            {
                CurrentSavedArticle = (Article)IsolatedStorageSettings.ApplicationSettings["CurrentSavedArticle"];

                UpdateSavedArticleMoveStates();
            }

            if (IsolatedStorageSettings.ApplicationSettings.Contains("CurrentVideo"))
            {
                CurrentVideo = (Video)IsolatedStorageSettings.ApplicationSettings["CurrentVideo"];
            }
        }

        private void SaveSettings()
        {
            IsolatedStorageSettings.ApplicationSettings["Settings"] = Settings;

            if (CurrentArticle != null)
            {
                IsolatedStorageSettings.ApplicationSettings["CurrentArticle"] = CurrentArticle;
            }

            if (CurrentSavedArticle != null)
            {
                IsolatedStorageSettings.ApplicationSettings["CurrentSavedArticle"] = CurrentSavedArticle;
            }

            if (CurrentVideo != null)
            {
                IsolatedStorageSettings.ApplicationSettings["CurrentVideo"] = CurrentVideo;
            }
        }

        private void UpdateArticleMoveStates()
        {
            CanMoveToPreviousArticle = false;
            CanMoveToNextArticle = false;

            for (int articleIndex = 0; articleIndex < Articles.Count; articleIndex++)
            {
                if (Articles[articleIndex].Id == CurrentArticle.Id)
                {
                    CanMoveToPreviousArticle = articleIndex > 0;
                    CanMoveToNextArticle = articleIndex + 1 != Articles.Count;

                    break;
                }
            }

            CanSaveCurrentArticle = !IsArticleAlreadySaved(CurrentArticle);
        }

        private void UpdateSavedArticleMoveStates()
        {
            CanMoveToPreviousSavedArticle = false;
            CanMoveToNextSavedArticle = false;

            for (int articleIndex = 0; articleIndex < SavedArticles.Count; articleIndex++)
            {
                if (SavedArticles[articleIndex].Id == CurrentSavedArticle.Id)
                {
                    CanMoveToPreviousSavedArticle = articleIndex > 0;
                    CanMoveToNextSavedArticle = articleIndex + 1 != SavedArticles.Count;

                    break;
                }
            }

            CanSaveCurrentArticle = !IsArticleAlreadySaved(CurrentArticle);
        }

        #region Notify Events

        private void NotifyDataRefreshed(DataType dataType, bool isSuccess)
        {
            EventHandler<DataRefreshedEventArgs> dataRefreshedEventHandler = OnDataRefreshed;

            if (dataRefreshedEventHandler != null)
            {
                dataRefreshedEventHandler(this, new DataRefreshedEventArgs(dataType, isSuccess));
            }
        }

        private void NotifyCurrentArticleChanged(Article article)
        {
            EventHandler<ArticleChangedEventArgs> articleChangedEventHandler = OnArticleChanged;

            if (articleChangedEventHandler != null)
            {
                articleChangedEventHandler(this, new ArticleChangedEventArgs(article));
            }
        }

        private void NotifyCurrentSavedArticleChanged(Article article)
        {
            EventHandler<ArticleChangedEventArgs> articleChangedEventHandler = OnSavedArticleChanged;

            if (articleChangedEventHandler != null)
            {
                articleChangedEventHandler(this, new ArticleChangedEventArgs(article));
            }
        }

        private void NotifyArticlesChanged()
        {
            EventHandler savedArticlesChangedEventHandler = OnSavedArticlesChanged;

            if (savedArticlesChangedEventHandler != null)
            {
                savedArticlesChangedEventHandler(this, EventArgs.Empty);
            }
        }

        #endregion

        #endregion
    }
}